package com.insight.controller;

import com.insight.common.enums.ExportFormat;
import com.insight.common.enums.ExportStatus;
import com.insight.dto.export.ExportCreateRequest;
import com.insight.dto.export.ExportResponse;
import com.insight.entity.DataSource;
import com.insight.entity.DictionaryExport;
import com.insight.entity.MetadataTable;
import com.insight.repository.MetadataTableRepository;
import com.insight.service.DataSourceService;
import com.insight.service.ExportService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * T097: 数据字典导出控制器
 * 提供数据字典导出的REST API
 *
 * @author Insight Team
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/exports")
@RequiredArgsConstructor
public class ExportController {

    private final ExportService exportService;
    private final DataSourceService dataSourceService;
    private final MetadataTableRepository metadataTableRepository;

    /**
     * 创建导出任务
     *
     * @param request 创建请求
     * @return 导出响应
     */
    @PostMapping
    public ResponseEntity<ExportResponse> createExport(
            @Valid @RequestBody ExportCreateRequest request) {

        log.info("创建导出任务: datasourceId={}, format={}",
                request.getDatasourceId(), request.getFormat());

        // 1. 验证数据源存在
        DataSource dataSource = dataSourceService.findById(request.getDatasourceId())
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "数据源不存在: id=" + request.getDatasourceId()
                ));

        // 2. 获取数据源的所有表
        List<MetadataTable> tables = metadataTableRepository.findByDataSourceId(request.getDatasourceId());

        // TODO: 应用schemaFilter和tableFilter过滤表
        // if (request.getSchemaFilter() != null) { ... }
        // if (request.getTableFilter() != null) { ... }

        // 3. 创建导出
        DictionaryExport export = exportService.exportDictionary(dataSource, tables, request.getFormat());

        // 4. 自动归档到MinIO(如果请求)
        if (Boolean.TRUE.equals(request.getAutoArchive()) &&
            export.getStatus() == ExportStatus.SUCCESS) {
            try {
                export = exportService.archiveToMinIO(export);
                log.info("导出文件已自动归档到MinIO: exportId={}", export.getId());
            } catch (IOException e) {
                log.warn("自动归档失败,但导出已成功: exportId={}", export.getId(), e);
            }
        }

        log.info("导出任务创建成功: exportId={}, status={}", export.getId(), export.getStatus());

        // 5. 返回响应
        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(toResponse(export));
    }

    /**
     * 获取导出详情
     *
     * @param id 导出ID
     * @return 导出响应
     */
    @GetMapping("/{id}")
    public ResponseEntity<ExportResponse> getExport(@PathVariable Long id) {
        log.debug("查询导出记录: id={}", id);

        DictionaryExport export = exportService.getExportById(id)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "导出记录不存在: id=" + id
                ));

        return ResponseEntity.ok(toResponse(export));
    }

    /**
     * 删除导出记录
     *
     * @param id 导出ID
     * @return 无内容响应
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteExport(@PathVariable Long id) {
        log.info("删除导出记录: id={}", id);

        // 验证存在
        exportService.getExportById(id)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "导出记录不存在: id=" + id
                ));

        // 删除
        exportService.deleteExport(id);

        log.info("导出记录删除成功: id={}", id);
        return ResponseEntity.noContent().build();
    }

    /**
     * 下载导出文件
     *
     * @param id 导出ID
     * @return 文件流或重定向到MinIO URL
     */
    @GetMapping("/{id}/download")
    public ResponseEntity<?> downloadExport(@PathVariable Long id) throws IOException {
        log.info("下载导出文件: id={}", id);

        // 1. 获取导出记录
        DictionaryExport export = exportService.getExportById(id)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "导出记录不存在: id=" + id
                ));

        // 2. 检查状态
        if (export.getStatus() == ExportStatus.FAILED) {
            throw new ResponseStatusException(
                    HttpStatus.NOT_FOUND,
                    "导出失败,无法下载"
            );
        }

        if (export.getStatus() == ExportStatus.EXPIRED &&
            !Boolean.TRUE.equals(export.getIsArchived())) {
            throw new ResponseStatusException(
                    HttpStatus.GONE,
                    "文件已过期且未归档"
            );
        }

        // 3. 生成下载URL
        String downloadUrl;
        try {
            downloadUrl = exportService.generateDownloadUrl(export);
        } catch (IOException e) {
            log.error("生成下载URL失败: exportId={}", id, e);
            throw new ResponseStatusException(
                    HttpStatus.NOT_FOUND,
                    "无法生成下载URL: " + e.getMessage()
            );
        }

        // 4. 区分本地文件和MinIO URL
        if (downloadUrl.startsWith("http://") || downloadUrl.startsWith("https://")) {
            // MinIO预签名URL - 重定向
            log.info("重定向到MinIO URL: exportId={}", id);
            return ResponseEntity
                    .status(HttpStatus.FOUND)
                    .header(HttpHeaders.LOCATION, downloadUrl)
                    .build();
        } else {
            // 本地文件 - 直接返回文件流
            if (export.getLocalPath() == null) {
                throw new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "本地文件不存在"
                );
            }

            Path filePath = Paths.get(export.getLocalPath());
            if (!Files.exists(filePath)) {
                throw new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "本地文件不存在: " + filePath
                );
            }

            Resource resource = new FileSystemResource(filePath);
            String contentType = getContentType(export.getFormat());

            log.info("返回本地文件: exportId={}, path={}", id, filePath);

            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(contentType))
                    .header(HttpHeaders.CONTENT_DISPOSITION,
                            "attachment; filename=\"" + export.getFileName() + "\"")
                    .body(resource);
        }
    }

    /**
     * 归档到MinIO
     *
     * @param id 导出ID
     * @return 导出响应
     */
    @PostMapping("/{id}/archive")
    public ResponseEntity<ExportResponse> archiveExport(@PathVariable Long id) throws IOException {
        log.info("归档导出到MinIO: id={}", id);

        // 1. 获取导出记录
        DictionaryExport export = exportService.getExportById(id)
                .orElseThrow(() -> new ResponseStatusException(
                        HttpStatus.NOT_FOUND,
                        "导出记录不存在: id=" + id
                ));

        // 2. 验证状态
        if (Boolean.TRUE.equals(export.getIsArchived())) {
            throw new ResponseStatusException(
                    HttpStatus.BAD_REQUEST,
                    "文件已归档"
            );
        }

        if (export.getStatus() != ExportStatus.SUCCESS) {
            throw new ResponseStatusException(
                    HttpStatus.BAD_REQUEST,
                    "只能归档成功的导出记录"
            );
        }

        // 3. 归档
        DictionaryExport archivedExport = exportService.archiveToMinIO(export);

        log.info("归档成功: exportId={}, minioPath={}", id, archivedExport.getMinioPath());

        return ResponseEntity.ok(toResponse(archivedExport));
    }

    /**
     * 清理过期文件
     *
     * @return 清理结果
     */
    @PostMapping("/cleanup")
    public ResponseEntity<Map<String, Object>> cleanupExpiredExports() {
        log.info("清理过期文件");

        int deletedCount = exportService.cleanupExpiredFiles();

        log.info("清理完成: deletedCount={}", deletedCount);

        Map<String, Object> response = new HashMap<>();
        response.put("deletedCount", deletedCount);
        response.put("message", deletedCount > 0
                ? "成功清理" + deletedCount + "个过期文件"
                : "没有过期文件需要清理");

        return ResponseEntity.ok(response);
    }

    /**
     * 获取导出列表
     *
     * @param datasourceId 数据源ID(可选)
     * @return 导出列表
     */
    @GetMapping
    public ResponseEntity<List<ExportResponse>> listExports(
            @RequestParam(required = false) Long datasourceId) {

        log.debug("查询导出列表: datasourceId={}", datasourceId);

        List<DictionaryExport> exports = exportService.getExportsByDataSource(datasourceId);

        List<ExportResponse> responses = exports.stream()
                .map(this::toResponse)
                .collect(Collectors.toList());

        return ResponseEntity.ok(responses);
    }

    /**
     * 获取导出统计信息
     *
     * @return 统计信息
     */
    @GetMapping("/statistics")
    public ResponseEntity<Map<String, Object>> getStatistics() {
        log.debug("查询导出统计信息");

        long successCount = exportService.countByStatus(ExportStatus.SUCCESS);
        long failedCount = exportService.countByStatus(ExportStatus.FAILED);
        long expiredCount = exportService.countByStatus(ExportStatus.EXPIRED);

        Map<String, Object> statistics = new HashMap<>();
        statistics.put("totalExports", successCount + failedCount + expiredCount);
        statistics.put("successfulExports", successCount);
        statistics.put("failedExports", failedCount);
        statistics.put("expiredExports", expiredCount);

        return ResponseEntity.ok(statistics);
    }

    /**
     * 转换为响应DTO
     */
    private ExportResponse toResponse(DictionaryExport export) {
        return ExportResponse.builder()
                .id(export.getId())
                .datasourceId(export.getDataSource().getId())
                .datasourceName(export.getDataSource().getName())
                .format(export.getFormat())
                .fileName(export.getFileName())
                .fileSize(export.getFileSize())
                .fileSizeFormatted(formatFileSize(export.getFileSize()))
                .expiresAt(export.getExpiresAt())
                .isArchived(export.getIsArchived())
                .status(export.getStatus())
                .createdAt(export.getCreatedAt())
                .isExpired(export.getExpiresAt() != null &&
                          export.getExpiresAt().isBefore(java.time.LocalDateTime.now()))
                .build();
    }

    /**
     * 格式化文件大小
     */
    private String formatFileSize(Long bytes) {
        if (bytes == null) {
            return null;
        }

        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.1f KB", bytes / 1024.0);
        } else if (bytes < 1024 * 1024 * 1024) {
            return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
        } else {
            return String.format("%.1f GB", bytes / (1024.0 * 1024.0 * 1024.0));
        }
    }

    /**
     * 获取Content-Type
     */
    private String getContentType(ExportFormat format) {
        switch (format) {
            case MARKDOWN:
                return "text/markdown";
            case HTML:
                return "text/html";
            case EXCEL:
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case PDF:
                return "application/pdf";
            default:
                return "application/octet-stream";
        }
    }
}
